define(function(require, exports, module) {

  // var Backbone = require('backbone');
  var $ = require('jquery');
  var _ = require('lodash');
  // var jsonview = require('jsonview');
  var tmpl = require('text!../tmpl.html');
  var littleCanvas = require('hy5b/frontend/canvas/littleCanvas')
  var util = require('hy5b/graph/index')
  var ui = require('hy5b/frontend/util')
  var graph = require('js/graph/graph')
  var flow = require('js/link/flow')
  var animation = require('hy5b/animation/index')
  var composite = require('tmpl/composite/index')
  
  const AColorPicker = require('AColorPicker');
  
  const paddingTop = 20
  const padding = 5

  const lightParticleRadius = 2
  const T = 2000

  let globalCompositeOperation = 'color'

  class ChangeColor {
    constructor(option, el) {
      this.option = option
      this.el = el
      const lc = new littleCanvas();

      this.width = 170
      this.height = 200

      el.appendChild(lc.pop())

      this.lc = lc

      this.card = new Image()
      this.card.src = '/assets/img/card.png'
      this.setIconSyle(this.option.icon)
      this.setMoldcStyle(this.option.moldc);
      this.setRenderStyle(this.option.render);

      this.t = 0;
    }

    step() {
      this.timer = requestAnimationFrame(this.step.bind(this))
      this.draw()
      if (this.t >= T) { //下次让animation返回一个最小周期
        this.t = 0
      } else {
        this.t += animation.FRAME60
      }
    }

    draw() {
      this.lc.setSize(this.width, this.height)

      let cardColor = this.option.moldc.card.color
      let dingColor = this.option.moldc.ding.color
      let moldColor = this.option.moldc.mold.color

      const dingColorArr = util.parse2Color(dingColor);

      let dingConfig = [{
        color: util.joinRgb(dingColorArr, 0.3),
        radius: 12
      }, {
        color: util.joinRgb(dingColorArr, 0.5),
        radius: 8
      }, {
        color: 'rgba(255,255,255,0.6)',
        radius: 3
      }]

      const renderColor = this.runRenderStyle()
      if (renderColor) {
        cardColor = renderColor
        moldColor = renderColor
        const renderColorArr = util.parse2Color(renderColor)
        dingConfig[0].color = util.joinRgb(renderColorArr, 0.3)
        dingConfig[1].color = util.joinRgb(renderColorArr, 0.5)
        dingConfig[2].color = util.joinRgb(dingColorArr, 0.6)

        const animateProps = this.renderAnimate.getStaticFrame(this.t)
        dingConfig[0].radius = animateProps.ding2.now
        dingConfig[1].radius = animateProps.ding1.now
        dingConfig[2].radius = 5
      }
      
      //背景
      let cardLc = this.getImageWithColor(this.card, cardColor, 159, 173)
      this.lc.draw(ctx => {
        ctx.drawImage(cardLc.pop(), padding, paddingTop, this.width - 2 * padding, this.height - padding - paddingTop)
        cardLc = null
      })
      
      //钉
      _.each(dingConfig, config => {
        this.lc.draw(ctx => {
          ctx.fillStyle = config.color
          ctx.arc(this.width / 2, paddingTop+2, config.radius, 0, Math.PI * 2)
          ctx.fill()
        })
      })

      const moldSize = this.width / 3

      let moldY = (this.height - paddingTop - padding) * 9 / 14 + paddingTop;
      let moldShadowR = moldSize / 3;

      if (this.option.moldc.mold.effect) {
        const animateProps = this.moldAnimate.getStaticFrame(this.t)
        moldY = animateProps.moldY.now
        moldShadowR = animateProps.moldShadowR.now
      }

      const moldCenterX = this.width / 2
      const moldCenterY = moldY
      const moldOffsetX = moldCenterX - moldSize / 2
      const moldOffsetY = moldCenterY - moldSize / 2

      //icon背景光晕
      graph.drawHalo(this.lc, moldCenterX, moldCenterY, moldShadowR, moldColor)
      
      //icon
      let moldLc = this.getImageWithColor(this.icon, moldColor, moldSize, moldSize)
      this.lc.draw(ctx => {
        ctx.shadowColor = "black";
        ctx.shadowBlur = 10;
        ctx.shadowOffsetY = 10;
        ctx.drawImage(moldLc.pop(), 0, 0, moldSize, moldSize, moldOffsetX, moldOffsetY, moldSize, moldSize)
        moldLc = null
      })

      const headLabel = this.option.label.head
      //标题文字
      if (headLabel.showText) {
        const headText = this.option.data.name || headLabel.text
        this.lc.draw(ctx => {
          ctx.font = ui.combineFont({
            fontStyle: headLabel.fontItalic ? 'italic' : '',
            fontWeight: headLabel.fontBold ? 'bold' : '',
            fontSize: headLabel.fontSize + 'px',
            fontFamily: headLabel.fontFamily
          })
          ctx.fillStyle = headLabel.fontColor
          ctx.textAlign = 'center'
          ctx.textBaseline = 'middle'
          ctx.fillText(headText, this.width / 2, (this.height - paddingTop - padding) / 4 + paddingTop)
        })
      }
      
      const footLabel = this.option.label.foot
      //脚文字
      if (footLabel.showText) {
        const footText = this.option.data.value || footLabel.text
        this.lc.draw(ctx => {
          ctx.font = ui.combineFont({
            fontStyle: footLabel.fontItalic ? 'italic' : '',
            fontWeight: footLabel.fontBold ? 'bold' : '',
            fontSize: footLabel.fontSize + 'px',
            fontFamily: footLabel.fontFamily
          })
          ctx.fillStyle = footLabel.fontColor
          ctx.textAlign = 'center'
          ctx.textBaseline = 'middle'
          ctx.fillText(footText, this.width / 2, (this.height - paddingTop - padding) * 15 / 16 + paddingTop)
        })
      }

      //背景动画
      if (this.option.moldc.card.effect) {
        const points = this.flowFrame.getCluster()
        graph.drawParticalLightLine(this.lc, points, cardColor, lightParticleRadius)
      }
      
    }

    runRenderStyle() {
      let dataType = 'string'
      let data = _.get(this.option, 'data.value')
      const renderStyle = this.option.render
  
      if (_.isEmpty(renderStyle) || !data) {
        return
      }
  
      const fieldModelMap = this.fieldModelMap
      if (this.dataSetting && this.dataSetting.value) {
        if (
          fieldModelMap &&
          fieldModelMap[this.dataSetting.value] &&
          fieldModelMap[this.dataSetting.value].type === 'Value'
        ) {
          dataType = 'number'
        } else {
          dataType = 'string'
        }
      }
      const {
        config = {
          min: 0,
          max: 100,
          precision: 1,
          values: null,
          styles: [undefined]
        },
        status = {
          values: null,
          styles: [undefined]
        }
      } = renderStyle
  
      if (dataType === 'number') {
        const { values, styles, min, max } = config
        const sortedValues = values ? [min, ...values, max] : [min, max]
        data = Number(data)
        let index = _.sortedIndex(sortedValues, data)
        if (data === min) {
          index = 1
        }
        const color = styles[index - 1]
        return color
      } else {
        const { values, styles } = status
        let index = -1
        index = values.indexOf(data)
        const color = styles[index]
        return color
      }
    }

    getImageWithColor(image, color, width, height, compositeOperation) {
      const compositeLc = new littleCanvas().setSize(width, height)
      compositeLc.ctx.drawImage(image, 0, 0, width, height)

      const colorArr = util.parse2Color(color);
      let offscrenLc = new littleCanvas().setSize(width, height)
      offscrenLc.ctx.drawImage(image, 0, 0, width, height)
      const imageData = offscrenLc.ctx.getImageData(0, 0, width, height);
      const pdata = imageData.data;
      let i = 0;
      while (i < pdata.length) {
        pdata[i] = colorArr[0];
        pdata[i+1] = colorArr[1];
        pdata[i+2] = colorArr[2];
        pdata[i+3] = colorArr[3] !== undefined ? colorArr[3] * pdata[i+3] : pdata[i+3];
        i+=4
      }
      offscrenLc.ctx.putImageData(imageData, 0, 0);
    
      compositeLc.ctx.globalCompositeOperation = compositeOperation || globalCompositeOperation
      compositeLc.draw(ctx => {
        ctx.drawImage(offscrenLc.pop(), 0, 0, width, height);
        offscrenLc = null
      })
      return compositeLc;
    }

    destroy() {
      if (this.timer) {
        cancelAnimationFrame(this.timer)
      }
    }

    setMoldcStyle(moldcStyle) {
      _.merge(this.option.moldc, moldcStyle);
      if (moldcStyle.card && moldcStyle.card.effect && !this.flowFrame) {
        const offset = lightParticleRadius;
        const points = [
          [padding+offset,paddingTop+offset],
          [padding+offset, this.height-padding-offset],
          [this.width-padding-offset,this.height-padding-offset],
          [this.width-padding-offset,paddingTop+offset],
          [padding+offset,paddingTop+offset]
        ]
        this.flowFrame = flow.animationPlus({
          points: points
        });
      }

      if (moldcStyle.mold && moldcStyle.mold.effect && !this.moldAnimate) {
        this.moldAnimate = new animation({
          props: {
            moldShadowR: {
              start: (this.width - 2 * padding) * 2 / 9,
              end: (this.width - 2 * padding) * 3 / 9,
              duration: 500,
              infinity: true,
              backtrace: true
            },
            moldY: {
              start: (this.height - paddingTop - padding) * 9 / 14 + paddingTop,
              end: (this.height - paddingTop - padding) * 17 / 28 + paddingTop,
              duration: 1000,
              infinity: true,
              backtrace: true
            }
          }
        })
      }
    }

    setIconSyle(iconStyle) {
      _.merge(this.option.icon, iconStyle);
      if (iconStyle.image && (!this.icon || this.icon.src !== iconStyle.image)) {
        this.icon = new Image()
        this.icon.src = iconStyle.image
      }
    }

    setLabel(labelStyle) {
      _.merge(this.option.label, labelStyle);
    }

    setRenderStyle(renderStyle) {
      _.merge(this.option.render, renderStyle)
      if (renderStyle.config || renderStyle.status) {
        this.renderAnimate = new animation({
          props: {
            ding1: {
              start: 0,
              end: 0,
              duration: 2000,
              mediacies: [[0, 1800, 10], [100]],
              infinity: true
            },
            ding2: {
              start: 0,
              end: 0,
              duration: 2000,
              mediacies: [[0, 1700, 16], [100, 1900, 0], [0]],
              infinity: true
            }
          }
        })
      }
    }

    render() {
      this.destroy()
      this.step()
    }
  }

  function main() {
    var $app = $('<div id="app">');
    $('#main').html($app);

    $app.append(tmpl);

    const cc = new ChangeColor({
      moldc: {
        card: {
          color: '#02a8f5',
          effect: true
        },
        ding: {
          color: 'rgb(253, 243, 37)'
        },
        mold: {
          color: '#cd0a0a',
          effect: false
        }
      },
      label: {
        head: {
          text: '组合模具',
          showText: true,
          fontSize: 18,
          fontFamily: 'Microsoft YaHei,PingFang SC',
          fontBold: true,
          fontItalic: false,
          fontColor: 'rgb(173, 246, 253)',
        },
        foot: {
          text: 'No problem',
          showText: true,
          fontSize: 10,
          fontFamily: 'Microsoft YaHei,PingFang SC',
          fontBold: false,
          fontItalic: false,
          fontColor: '#ccc',
        }
      },
      icon: {
        image: '/assets/img/mold.png'
      },
      render: {
        status: {
          values: ['No Problem'],
          styles: ['#00ff00']
        }
      },
      data: {
        name: 'A++',
        value: 'No Problem'
      }
    }, $('#change-color')[0])

    cc.render();

    AColorPicker.from('#color-card-picker', {
      showHSL: false,
      showAlpha: true
    }).on('change', (picker, color) => {
      cc.setMoldcStyle({
        card: {
          color: color
        }
      })
      cc.render()
    });

    AColorPicker.from('#color-icon-picker', {
      showHSL: false,
      showAlpha: true
    }).on('change', (picker, color) => {
      cc.setMoldcStyle({
        mold: {
          color: color
        }
      })
      cc.render()
    });

    composite(function setVal(val) { globalCompositeOperation = val }, $app)
  }

  return main;
})
